﻿namespace TamTam.SharePoint2010.Social {

    #region [ Usings ]

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Microsoft.Office.Server.UserProfiles;
    using Microsoft.Office.Server.ActivityFeed;
    using Microsoft.SharePoint;
    using System.ServiceModel.Syndication;
    using System.Xml;

    #endregion

    /// <summary>
    /// Activity Feed gatherer and downloader for Twitter
    /// </summary>
    public class TwitterActivityFeed {

        #region [ Consts ]

        private const string TwitterProfileField = "TwitterAccountName";
        private const string ActivityApplicationName = "Twitter";
        private const string ActivityTypeName = "Tweet";

        #endregion

        /// <summary>
        /// Check if a UserProfile is Twitter enabled!
        /// </summary>
        /// <param name="profile">the userprofile to check</param>
        /// <returns>true if the user has a twitter account filled in his profile</returns>
        public static bool ProfileHasTwitter(UserProfile profile) {
            bool result = false;
            if (profile != null) {
                if (profile[TwitterProfileField] != null) {
                    if (!string.IsNullOrEmpty(profile[TwitterProfileField].Value as string)) {
                        result = true;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Register the ActivityApplication Twitter to the Portal(SPSite)'s User Profile Application
        /// </summary>
        /// <param name="site">portal/context</param>
        public static bool RegisterTwitterActivityApplication(SPSite site) {
            bool ok = false;
            SPServiceContext ctx = SPServiceContext.GetContext(site);
            if (ctx != null) {
                //UserProfileManager upm = new UserProfileManager(ctx);
                //UserProfile user = upm.GetUserProfile(true);
                ActivityManager am = new ActivityManager(null, ctx);

                if (am.PrepareToAllowSchemaChanges()) {
                    try {
                        ActivityApplication twitterApp = am.ActivityApplications["Twitter"];

                        if (twitterApp == null) {
                            twitterApp = am.ActivityApplications.Create("Twitter");
                            twitterApp.Commit();
                        }

                        ActivityType tweet = twitterApp.ActivityTypes["Tweet"];
                        if (tweet == null) {
                            tweet = twitterApp.ActivityTypes.Create("Tweet");
                        }
                        tweet.GatheringInterval = 300;
                        tweet.ActivityTypeNameLocStringResourceFile = twitterApp.ApplicationName;
                        tweet.ActivityTypeNameLocStringName = "Activity_Name";
                        tweet.IsPublished = true;
                        tweet.IsConsolidated = true;
                        tweet.AllowRollup = true;
                        tweet.Commit();

                        ActivityTemplate tweetTemplate = tweet.ActivityTemplates[ActivityTemplatesCollection.CreateKey(false)];
                        if (tweetTemplate == null) {
                            tweetTemplate = tweet.ActivityTemplates.Create(false);
                        }
                        tweetTemplate.TitleFormatLocStringResourceFile = twitterApp.ApplicationName;
                        tweetTemplate.TitleFormatLocStringName = "Activity_Tweet";
                        tweetTemplate.Commit();
                        ok = tweetTemplate != null;
                    }
                    catch (Exception exc) {

                    }
                }
            }
            return ok;
        }

        /// <summary>
        /// Import the Twitter updates for a UserProfile and
        /// publish them to the activity feed.
        /// </summary>
        /// <param name="site">Portal/context</param>
        /// <param name="profile">the user profile to import twitter updates for</param>
        /// <returns>nr of items published</returns>
        public static int Import(SPSite site, UserProfile profile) {
            int result = 0;

            if (ProfileHasTwitter(profile)) {
                // user should have a value in his profile:
                string twitterUserName = profile[TwitterActivityFeed.TwitterProfileField].Value as string;
                if (!string.IsNullOrEmpty(twitterUserName)) {
                    // activity manager to the social stuff
                    ActivityManager am = new ActivityManager(profile, SPServiceContext.GetContext(site));
                    // twitter
                    ActivityType tweet = am.ActivityTypes[TwitterActivityFeed.ActivityTypeName];

                    // last twitter update was on:
                    DateTime twitterLatestItem = SocialUpdateStatusList.GetLastItemDateTime(site, tweet, profile);

                    string newTwitterLastId = null;

                    // download the user's timeline as RSS feed
                    using (XmlReader reader = XmlReader.Create(
                        string.Format("http://www.twitter.com/statuses/user_timeline/{0}.rss", twitterUserName))) {
                        // convert download to a SyndicationFeed
                        SyndicationFeed twitterfeed = SyndicationFeed.Load(reader);

                        // order them the correct way
                        twitterfeed.Items.OrderByDescending(item => item.PublishDate);
                        List<ActivityEvent> items = new List<ActivityEvent>();
                        // loop through the RSS feed items
                        foreach (SyndicationItem item in twitterfeed.Items) {

                            // item is newer then the last one we published
                            if (item.PublishDate.DateTime.ToLocalTime() > twitterLatestItem) {
                                // store the newest update for later on!
                                if (newTwitterLastId == null) {
                                    newTwitterLastId = item.Id;
                                    SocialUpdateStatusList.SetLastItemId(site, tweet, profile, newTwitterLastId);
                                }
                                // make the data complete
                                if (item.LastUpdatedTime.DateTime <= DateTime.MinValue)
                                    item.LastUpdatedTime = item.PublishDate;

                                // store the twitter update to the user's activity feed
                                ActivityEvent activityEvent = TwitterActivityFeed.GenerateActivityEvent(am, profile, tweet, item);
                                items.Add(activityEvent);
                            }
                            else {
                                break;
                            }
                        }
                        // publish all the udates to collegueas and such
                        TwitterActivityFeed.MulticastPublishedEvents(am, items);
                        am.Dispose();
                        result = items.Count;
                    }
                }
            }
            return result;
        }

        /// <summary>
        /// Generate a Activity Event based on a RSS Feed item
        /// </summary>
        /// <param name="am"></param>
        /// <param name="profile"></param>
        /// <param name="atype"></param>
        /// <param name="item"></param>
        /// <returns></returns>
        internal static ActivityEvent GenerateActivityEvent(ActivityManager am, UserProfile profile, ActivityType atype, SyndicationItem item) {

            ActivityEvent activityEvent = null;

            if (am != null) {
                if (atype != null && profile != null) {
                    long atid = atype.ActivityTypeId;
                    if (item != null) {

                        Entity owner = new MinimalPerson(profile).CreateEntity(am);
                        Entity publisher = new MinimalPerson(profile).CreateEntity(am);

                        activityEvent = ActivityEvent.CreateActivityEvent(am, atid, owner, publisher);

                        activityEvent.ItemPrivacy = (int)Privacy.Public;
                        activityEvent.Name = atype.ActivityTypeName;
                        activityEvent.Owner = owner;
                        activityEvent.Publisher = publisher;
                        activityEvent.PublishDate = item.LastUpdatedTime.DateTime;
                        activityEvent.LastUpdateTime = item.LastUpdatedTime.DateTime;

                        if (item.Links.Count > 0) {
                            activityEvent.Link = new Link();
                            activityEvent.Link.Href = item.Links[0].Uri.AbsoluteUri;
                            activityEvent.Link.Name = "tweeted";
                            activityEvent.Link.Value = activityEvent.Link.Name;
                        }

                        string text = item.Summary.Text;
                        if (text.IndexOf(':') > 0)
                            text = text.Substring(text.IndexOf(':') + 2);

                        activityEvent.Value = text;

                        activityEvent.Commit();
                    }
                }
            }
            return activityEvent;
        }

        /// <summary>
        /// publish a collection of activity events to the network of a userprofile
        /// </summary>
        /// <param name="am"></param>
        /// <param name="activityEvents"></param>
        internal static void MulticastPublishedEvents(ActivityManager am, List<ActivityEvent> activityEvents) {
            if (activityEvents.Count == 0)
                return;
            List<long> publishers = new List<long>();
            foreach (ActivityEvent activityEvent in activityEvents) {
                if (!publishers.Contains(activityEvent.Owner.Id))
                    publishers.Add(activityEvent.Owner.Id);
            }
            Dictionary<long, MinimalPerson> owners;
            Dictionary<long, List<MinimalPerson>> colleaguesOfOwners;
            ActivityFeedGatherer.GetUsersColleaguesAndRights(am, publishers, out owners, out colleaguesOfOwners);
            Dictionary<long, List<ActivityEvent>> eventsPerOwner;
            ActivityFeedGatherer.MulticastActivityEvents(am, activityEvents, colleaguesOfOwners, out eventsPerOwner);
            List<ActivityEvent> eventsToMulticast;
            ActivityFeedGatherer.CollectActivityEventsToConsolidate(eventsPerOwner, out eventsToMulticast);
            WriteEvents(am, eventsToMulticast);
        }

        private static void WriteEvents(ActivityManager am, List<ActivityEvent> events) {
            int startIndex = 0;
            while (startIndex + am.MaxEventsPerBatch < events.Count) {
                ActivityFeedGatherer.BatchWriteActivityEvents(events, startIndex, am.MaxEventsPerBatch);
                startIndex += am.MaxEventsPerBatch;
            }
            ActivityFeedGatherer.BatchWriteActivityEvents(events, startIndex, events.Count - startIndex);
        }
    }
}